#ifndef MQCOMM_MQ_HELPER_HPP_
#define MQCOMM_MQ_HELPER_HPP_
#include "../mqcomm/proto/mq_msg.pb.h"
#include "mq_logger.hpp"
#include <iostream>
#include <string>
#include <utility>
#include <vector>
#include <sqlite3.h>
#include <atomic>
#include <random>
#include <sstream>
#include <iomanip>
#include <sys/stat.h>
#include <fstream>
#include <cstring>
#include <cerrno>
#include <unordered_map>

namespace helper
{
    using ArgsMap = google::protobuf::Map<std::string, std::string>;

    class SqliteHelper
    {
        typedef int (*SqliteCallback)(void *, int, char **, char **);

    public:
        SqliteHelper(const std::string &dbfile)
            : dbfile_(dbfile), handler_(nullptr) {}

        bool open(int save_level = SQLITE_OPEN_FULLMUTEX)
        {
            int ret = sqlite3_open_v2(dbfile_.c_str(), &handler_,
                                      SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | save_level, nullptr);
            if (ret != SQLITE_OK)
            {
                LOG_FATAL << "create/open sqlite database failed: "
                          << sqlite3_errmsg(handler_) << "\n";
                return false;
            }
            return true;
        }

        bool exec(const std::string &sql, SqliteCallback callback, void *arg)
        {
            int ret = sqlite3_exec(handler_, sql.c_str(), callback, arg, nullptr);
            if (ret != SQLITE_OK)
            {
                LOG_ERROR << "execut sql \"" << sql << "\" failed: "
                          << sqlite3_errmsg(handler_) << "\n";
                return false;
            }
            return true;
        }

        void close()
        {
            if (handler_) sqlite3_close_v2(handler_);
        }

    private:
        std::string dbfile_; // 操作文件名
        sqlite3 *handler_;   // 数据库句柄
    };

    class StringHelper
    {
    public:
        static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> *result)
        {
            size_t pos, idx = 0;
            while (idx < str.size())
            {
                pos = str.find(sep, idx);
                if (pos == std::string::npos)
                {
                    result->emplace_back(str.substr(idx));
                    break;
                }
                if (pos != idx) result->emplace_back(str.substr(idx, pos - idx));
                idx = pos + sep.size();
            }
            return result->size();
        }

        static std::string serializeToString(const ArgsMap& args)
        {
            std::string args_str = "";
            for(auto it = args.begin(); it != args.end(); ++it)
                args_str += it->first + "=" + it->second + "&";
            return std::move(args_str);
        }

        static void deserializeFromString(const std::string& args_str, ArgsMap* args)
        {
            std::vector<std::string> result;
            const std::string sep = "&";
            StringHelper::split(args_str, sep, &result);
            for(auto& str : result)
            {
                size_t pos = str.find('=');
                std::string key = str.substr(0, pos);
                std::string val = str.substr(pos + 1);
                args->insert({key, val});
            }   
        }
    };

    class UUIDHelper
    {
    public:
        static std::string uuid()
        {
            std::random_device rand;
            std::mt19937_64 gernator(rand());                        // 通过梅森旋转算法，生成伪随机数
            std::uniform_int_distribution<int> distribution(0, 255); // 限定随机范围

            // 约定：8组随机数，每组1字节的16进制
            std::stringstream ss;
            for (int i = 0; i < 8; ++i)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(gernator);
                if (i == 3 || i == 5 || i == 7) ss << '-';
            }

            // 加入自增id, 取出8字节序号
            static std::atomic_size_t seq(1);
            size_t num = seq.fetch_add(1);
            for (int i = 7; i >= 0; --i)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> (i * 8)) & 0xFF);
                if (i == 6) ss << '-';
            }
            return ss.str();
        }
    };

    class FileHelper
    {
        static const int file_mode = 0775;
    public:
        FileHelper(const std::string& filename) : filename_(filename) {}
        bool exists()
        {
            struct stat st;
            return stat(filename_.c_str(), &st) == 0;   // On success, zero is returned
        }
        size_t size()
        {
            struct stat st;
            int ret = stat(filename_.c_str(), &st);
            if(ret < 0) return 0;
            return st.st_size;          // Total size, in bytes 
        }
        bool read(char* data, size_t offset, size_t len)
        {
            // 1. 打开文件
            std::ifstream ifs(filename_);
            if(!ifs.is_open())
            {
                LOG_FATAL << "open \"" << filename_ << " \" fail!\n";
                return false;
            }
            // 2. 定位指定偏移量读取
            ifs.seekg(offset, std::ios::beg);   // 定位到offset偏移量

            // 3. 读数据
            ifs.read(data, len);
            if(ifs.good() == false) 
            {
                LOG_ERROR << "\"" << filename_ << "\" file read fail!\n";
                ifs.close();
                return false;
            }
            // 4. 关闭文件
            ifs.close();
            return true;
        }
        bool read(std::string* data)
        {
            size_t filesize = this->size();
            data->resize(filesize + 1);
            return read(&(*data)[0], 0, filesize);
        }
        bool write(const char* data, size_t offset, size_t len)
        {
            // 1. 打开文件
            std::fstream fs(filename_, std::ios::binary | std::ios::in | std::ios::out);
            if(!fs.is_open())
            {
                LOG_FATAL << "open \"" << filename_ << " \" fail!\n";
                return false;
            }
            // 2. 定位指定偏移量读取
            fs.seekg(offset, std::ios::beg);   // 定位到offset偏移量
            // 3. 写数据
            fs.write(data, len);
            if(fs.good() == false) 
            {
                LOG_ERROR << "\"" << filename_ << "\" file write fail!\n";
                fs.close();
                return false;
            }
            // 4. 关闭文件
            fs.close();
            return true;
        }
        bool write(const std::string& data)
        {
            return write(data.c_str(), 0, data.size());
        }
        bool rename(const std::string& newname)
        {
            // On success, zero is returned. \
               On error, -1 is returned, and errno is set appropriately.
            return ::rename(filename_.c_str(), newname.c_str()) == 0;
        }
        static std::string parentDirectory(const std::string& pathname)
        {
            // /aaa/bbb/ccc/ddd/t.txt
            size_t pos = pathname.find_last_of('/');
            if(pos == std::string::npos)  return std::move("./");
            return std::move(pathname.substr(0, pos));
        }
        static bool createFile(const std::string& pathname)
        {
            std::ofstream ofs(pathname, std::ios::binary | std::ios::app);  // ios::app追加，避免截断覆盖
            if(!ofs.is_open())
            {
                LOG_FATAL << "create \"" << pathname << " \" fail!\n";
                return false;
            }
            ofs.close();
            return true;
        }
        static bool removeFile(const std::string& pathname)
        {     
            // It calls unlink(2) for files, and rmdir(2) for directories.
            // On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
            return ::remove(pathname.c_str()) == 0;
        }
        static bool createDirectory(const std::string& pathname)
        {
            // /aaa/bbb/ccc/ddd/t.txt
            size_t pos, idx = 0;
            while (idx < pathname.size())
            {
                pos = pathname.find('/', idx);
                if(pos == std::string::npos) 
                    return mkdir(pathname.c_str(), file_mode) == 0;
                
                std::string subpath = pathname.substr(0, pos);
                int ret = mkdir(subpath.c_str(), file_mode);
                if(ret != 0 && errno != EEXIST) 
                {
                    LOG_FATAL << "create dir \"" << pathname << "\" fail, " << strerror(errno) << '\n';
                    return false;
                }
                idx = pos + 1;
            }
            return true;
        }
        static bool removeDirectory(const std::string& pathname)
        {
            std::string cmd = "rm -rf " + pathname;
            return system(cmd.c_str()) != -1;
        }
    private:
        std::string filename_;
    };
}
#endif